<template>
  <article class="page page-screencap-web">
    <h4 class="text-center">网页录屏</h4>
    <div class="page-body row">
      <div class="col-4">
        <fieldset>
          <legend>参数配置</legend>
          <div class="fieldset-content">
            <div class="main">
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">网页地址</div>
                <div class="row-flex-main"><input class="form-control" type="text" v-model.trim="webUrl"
                    placeholder="请输入网页地址" /></div>
                <div class="row-flex-suffix"><x-button @click="iframeUrl=webUrl">访问</x-button></div>
              </div>
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">保存目录</div>
                <div class="row-flex-main"><input class="form-control" type="text" v-model="toPath" /></div>
                <div class="row-flex-suffix"><x-button @click="setToPath">选择</x-button></div>
              </div>
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">保存频率</div>
                <div class="row-flex-main">
                  <div class="input-group">
                    <input class="form-control" type="number" v-model.trim="interval" min="1" max="60"
                      placeholder="间隔多久保存一次数据" />
                    <div class="input-group-append">
                      <span class="input-group-text">分钟</span>
                    </div>
                  </div>
                </div>
              </div>
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">平台类型</div>
                <div class="row-flex-main">
                  <div class="form-group">
                    <select class="form-control" v-model="platformType" placeholder="平台类型">
                      <option v-for="(v,i) in platformTypeList" :key="i" :value="v.value">{{v.label}}</option>
                    </select>
                  </div>
                </div>
              </div>
              <div class="row-flex" align="center" v-if="platformType==='cc'">
                <div class="row-flex-prefix">倍速播放</div>
                <div class="row-flex-main">
                  <div class="input-group">
                    <input class="form-control" type="number" v-model.trim="playbackRate" min="0.1" max="16"
                      placeholder="网页视频播放速度" />
                  </div>
                </div>
              </div>
            </div>
            <div class="bottom">
              <x-button type="primary" block @click="clickHandler">{{ recording > 0 ? '停止录制' : '开始录制'}}</x-button>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="col-8">
        <fieldset>
          <legend>网页区域</legend>
          <div class="fieldset-content" ref="fieldsetContent">
            <!-- <iframe ref="iframe" :src="iframeUrl"></iframe> -->
            <webview ref="webview" :src="iframeUrl" disablewebsecurity nodeintegration></webview>
            <div v-show="iframeUrl" class="iframe-btn badge rounded-pill bg-danger" @click="clickHandler">
              {{ recording > 0 ? '停止录制' : '开始录制'}}
            </div>
            <div v-show="recording > 0" class="record-duration">
              {{ formatRecordTime }}
            </div>
          </div>
        </fieldset>
      </div>
    </div>
  </article>
</template>

<script>
const fs = global.require('fs')
const path = global.require('path')
const {exec} = global.require('child_process')
const { remote } = global.require('electron')
const { dialog } = remote
const selfWindow = remote.getCurrentWindow()
let MediaRecorder = global.MediaRecorder

export default {
  name: 'screencap-web',
  data() {
    return {
      webUrl: '',
      iframeUrl: '',
      interval: 10,
      toPath: this.$sharedObject('desktopPath'),
      videoRecorder: null,//MediaRecorder实例
      recording: 0,//是否正在录像
      recordDuration: 0,//录制时长
      recordTimer: null,//录制计时器
      mediaStream: '',
      videoWriter: null,
      videoName: '',
      playbackRate: 1,
      platformType: 'normal',
      platformTypeList: [
        {label: '不区分', value: 'normal'},
        {label: 'CC视频直播回放', value: 'cc'},
      ],
      loadingCount: 0
    }
  },
  computed: {
    // 秒转时分秒
    formatRecordTime() {
      let result = this.recordDuration
      let h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600)
      let m = Math.floor((result / 60 % 60)) < 10 ? '0' + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60))
      let s = Math.floor((result % 60)) < 10 ? '0' + Math.floor((result % 60)) : Math.floor((result % 60))
      result = `${h}:${m}:${s}`
      return result
    }
  },
  methods: {
    // 设置下载目录
    setToPath() {
      let filePaths = dialog.showOpenDialogSync(selfWindow, {
        title: '选择目录',
        buttonLabel: '确定',
        defaultPath: this.$sharedObject('desktopPath'),
        properties: ['openDirectory']
      })
      if (filePaths instanceof Array && filePaths.length > 0) {
        this.toPath = filePaths[0]
      }
    },
    // 点击按钮
    clickHandler() {
      if (this.recording > 0) {
        this.recordStop()
      } else {
        this.recordStart()
      }
    },
    // 开始录制
    recordStart() {
      this.stopRecordVideo()
      this.$refs.fieldsetContent && this.$refs.fieldsetContent.requestFullscreen()
      this.getStreamSelf().then(this.getStreamBySource).then(() => {
        // 初始化MediaRecorder
        let mimeType = MediaRecorder.isTypeSupported('video/webm; codecs=vp9') ? 'video/webm; codecs=vp9' : 'video/webm'
        this.videoRecorder = new MediaRecorder(this.mediaStream, {mimeType})
        // 结束后获取数据
        this.videoRecorder.ondataavailable = (e) => {
          this.data2file(e)
        }
        // 开始
        this.videoRecorder.onstart = () => {
          this.videoWriter && this.videoWriter.close()
          this.videoWriter = null
        }
        // 结束
        this.videoRecorder.onstop = (e) => {
          this.data2file(e, true)
        }
        // 监听错误
        this.videoRecorder.onerror = (err) => {
          console.log(err)
          this.recording = 0
        }
        this.$nextTick(() => {
          this.startRecordVideo()
        })
      })
    },
    data2file (e) {
      let targetFilePath = path.join(this.toPath, `${this.videoName}.webm`)
      let writer = this.videoWriter
      if (!this.videoWriter) {
        writer = this.videoWriter = fs.createWriteStream(targetFilePath)
      }
      if (e.data && e.data.size) {
        let blob = new Blob([e.data], {type: 'video/webm'})
        let fileReader = new FileReader()
        fileReader.readAsArrayBuffer(blob)
        fileReader.onloadend = (e) => {
          // 保存视频
          writer.write(Buffer.from(e.target.result), () => {
            if (this.recording === 0) {
              this.videoWriter && this.videoWriter.close()
              this.videoWriter = null
              this.resultHandler()
            }
          })
        }
      }
    },
    // 结束录制
    recordStop() {
      if (document.webkitIsFullScreen) {
				document.exitFullscreen()
			}
      this.stopRecordVideo()
    },
    // 获取本程序窗口的镜像流数据
    getStreamSelf () {
      return window.electron.desktopCapturer.getSources({types: ['window', 'screen']})
      .then((sources) => {
        this.screensList = sources || []
        let source = this.screensList.find(item => item.name === document.title)
        return source
      })
      .catch((error) => {
        console.log('getStreamSelf:', error)
      })
    },
    // 获取指定窗口的镜像流数据
    getStreamBySource (source) {
      return navigator.mediaDevices.getUserMedia({
        audio: false,
        video: {
          mandatory: {
            chromeMediaSource: 'desktop',
            chromeMediaSourceId: source.id
          }
        }
      }).then((stream) => {
        this.mediaStream = stream
        return stream
      }).catch((error) => {
        if (error.name === 'NotReadableError') {
          this.stopStream()
        } else {
          console.log('getStreamBySource:', error)
        }
      })
    },
    // exec
    execPromise (command) {
      return new Promise((resolve, reject) => {
        exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {
          if (error) { reject(error) }
          console.error(`stderr: ${stderr}`)
          console.error(`stdout: ${stdout}`)
          resolve(stdout)
        })
      })
    },
    // 开始录像
    startRecordVideo() {
      if (this.recording) return
      let taskObj = Promise.resolve() 
      if (this.platformType === 'cc' && this.$refs.webview) {
        taskObj = this.$refs.webview.executeJavaScript(`
          $.DW.playbackRate(${this.playbackRate});
        `)
      }
      taskObj.then(() => {
        this.videoName = this.$jstools.formatDate(new Date(), 'yyyyMMddhhmmss')
        this.videoRecorder.start(this.interval * 60000)
        this.recording = 1
        this.recordTimer = setInterval(() => {
          this.recordDuration++
        }, 1000)
      })
    },
    // 停止录像
    stopRecordVideo() {
      if (!this.recording) return
      this.videoRecorder.stop()
      this.recording = 0
      this.recordTimer = clearInterval(this.recordTimer)
      this.recordDuration = 0
    },
    resultHandler () {
      if (this.platformType === 'cc') {
        let fromFilePath = path.join(this.toPath, `${this.videoName}.webm`)
        let toFilePath = path.join(this.toPath, `${this.videoName}_fix.mp4`)
        let command = `ffmpeg.exe -i ${fromFilePath} -filter:v "setpts=${this.playbackRate}*PTS" ${toFilePath}`
        this.loadingCount++
        this.execPromise(command).then(() => {
          this.$toast('视频转码成功')
        }).catch(() => {
          this.$toast('视频转码失败')
        }).finally(() => {
          this.loadingCount--
        })
      }
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";

.page-screencap-web {
  display: flex;
  flex-direction: column;

  .row-flex {
    &+.row-flex {
      margin-top: 1em;
    }

    .row-flex-prefix {
      min-width: 5em;
      text-align: right;
    }

    .row-flex-main {
      text-align: left;
    }
  }

  .page-body {
    height: 0;
    flex-grow: 1;
    overflow: hidden;
    position: relative;

    fieldset {
      height: 100%;
    }

    .fieldset-content {
      height: calc(100% - 2em);
      display: flex;
      flex-direction: column;
      position: relative;

      .main {
        height: 0;
        flex-grow: 1;
        overflow: hidden auto;
      }

      .bottom {
        .x-button {
          margin-top: .5em;
        }
      }

      iframe {
        display: block;
        width: 100%;
        height: 100%;
        background-color: #f5f5f5;
      }
      webview {
        height: 100%;
        background-color: #f5f5f5;
      }
			.iframe-btn {
				position: absolute;
				top: 5px;
				right: 5px;
				cursor: pointer;
			}
      .record-duration {
        position: absolute;
				top: 25px;
				right: 5px;
        font-weight: bold;
        color: red;
        -webkit-text-stroke: 1px #f5f5f5;
      }
    }
  }
}
</style>
